﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using eCitizen.DataContracts;
using eCitizen.eAlerts.Constants;

namespace eCitizen.eAlerts.Utilities
{
    // This class is responsible for parsing the file records
    public class RequestParser
    {
        private ServiceInfo _currentService = null;
        private SftpConfiguration _currentConfiguration = null;

        public RequestParser(SftpConfiguration currentConfigurationInfo, ServiceInfo currentServiceInfo)
        {
            _currentService = currentServiceInfo;
            _currentConfiguration = currentConfigurationInfo;
        }

        public bool TryParseRequestRecords(StreamReader eAlertRecord, out List<RequestEntry> parsedRecords)
        {
            bool isParseSuccessful = true;
            string errorMessage = null;
            string alertRecord = null;
            RequestEntry eAlertRequest = null;
            List<RequestEntry> inboundAlertRequests = new List<RequestEntry>();

            while (eAlertRecord.Peek()  != -1 && isParseSuccessful)
            {
                alertRecord = eAlertRecord.ReadLine();

                if (!String.IsNullOrEmpty(alertRecord))
                {
                    string[] eAlertInboundRecord = SplitCSV(alertRecord).ToArray();;

                    // Check the record count
                    isParseSuccessful = (eAlertInboundRecord.Length == 10 || eAlertInboundRecord.Length == 17);

                    if (isParseSuccessful)
                    {
                        #region "Parse the record line"

                        eAlertRequest = new RequestEntry();

                        // Data Segment 1 : Service ID
                        eAlertRequest.ServiceId = eAlertInboundRecord[0];

                        // Data Segment 2 : Transaction ID
                        eAlertRequest.TransactionId = eAlertInboundRecord[1];

                        // Data Segment 3 : Agency ID
                        eAlertRequest.AgencyId = eAlertInboundRecord[2];

                        // Data Segment 4 : NRIC
                        eAlertRequest.TargetNricOrAll = eAlertInboundRecord[3];

                        // Data Segment 5 : Description
                        eAlertRequest.MessageDescription = eAlertInboundRecord[4].Replace("\"","");

                        // Data Segment 6 : URL
                        eAlertRequest.Url = eAlertInboundRecord[5];

                        // Data Segment 7 : Date Due
                        DateTime ParsedDateDue = new DateTime();
                        if (isParseSuccessful) isParseSuccessful = DateTime.TryParse(eAlertInboundRecord[6], out ParsedDateDue);
                        if (isParseSuccessful) eAlertRequest.DateDue = ParsedDateDue;

                        // Data Segment 8 : Date Expired
                        DateTime ParsedDateExpired = new DateTime();
                        if (isParseSuccessful) isParseSuccessful = DateTime.TryParse(eAlertInboundRecord[7], out ParsedDateExpired);
                        if (isParseSuccessful) eAlertRequest.DateExpired = ParsedDateExpired;

                        // Data Segment 9 : Amount Dued
                        Decimal ParsedAmountDued = 0;
                        if (isParseSuccessful) isParseSuccessful = Decimal.TryParse(eAlertInboundRecord[8], out ParsedAmountDued);
                        if (isParseSuccessful) eAlertRequest.AmountDue = ParsedAmountDued;

                        // Data Segment 10: Payment Status
                        eAlertRequest.PaymentStatus = eAlertInboundRecord[9];

                        if (eAlertInboundRecord.Length > 10)
                        {
                            // Data Segment 11: Gender
                            eAlertRequest.Gender = eAlertInboundRecord[10];

                            // Data Segment 12: Birth Start Date
                            DateTime ParsedBirthStartDate = new DateTime();
                            if (isParseSuccessful) isParseSuccessful = DateTime.TryParse(eAlertInboundRecord[11], out ParsedBirthStartDate);
                            if (isParseSuccessful) eAlertRequest.BirthStartDate = ParsedBirthStartDate;

                            // Data Segment 13: Birth End Date
                            DateTime ParsedBirthEndDate = new DateTime();
                            if (isParseSuccessful) isParseSuccessful = DateTime.TryParse(eAlertInboundRecord[12], out ParsedBirthEndDate);
                            if (isParseSuccessful) eAlertRequest.BirthEndDate = ParsedBirthEndDate;

                            // Data Segment 14: Marital Status
                            eAlertRequest.MartialStatus = eAlertInboundRecord[13];

                            // Data Segment 15: Residential Status
                            eAlertRequest.ResidentialStatus = eAlertInboundRecord[14];

                            // Data Segment 16: Employment Status
                            eAlertRequest.EmploymentStatus = eAlertInboundRecord[15];

                            // Data Segment 17: Job Profile
                            eAlertRequest.JobProfile = eAlertInboundRecord[16];

                        }

                        if (isParseSuccessful)
                        {
                            inboundAlertRequests.Add(eAlertRequest);
                        }
                        else
                        {
                            errorMessage = ErrorMessageConstants.SendAlertService_InvalidDataFormat;
                        }

                        #endregion
                    }
                    else
                    {
                        errorMessage = ErrorMessageConstants.SendAlertService_InvalidRequestFileFormat;
                    }
                }
                
            }

            parsedRecords = inboundAlertRequests;

            // Request file parsing is not successsful
            if (!isParseSuccessful) SendErrorLog(errorMessage);

            eAlertRecord.Close();
            return isParseSuccessful;
        
        }

        public bool ValidateParsedRecords(List<RequestEntry> parsedRecords,int checkSumValue)
        {
            bool isParseSuccessful = true;

            // Check with CheckSumValue
            isParseSuccessful = parsedRecords.Count() == checkSumValue;

            if (!isParseSuccessful) SendErrorLog(ErrorMessageConstants.SendAlertService_RecordcountMismatched);

            // Check the records to ensure that the ServiceId is in accordance with the Current Service it is serving
            if (isParseSuccessful)
            {
                isParseSuccessful = parsedRecords.Where(c => c.ServiceId.Equals(_currentService.ServiceId, StringComparison.CurrentCultureIgnoreCase)).Count() == checkSumValue;
                if (!isParseSuccessful) SendErrorLog(ErrorMessageConstants.SendAlertService_InvalidServiceCode);
            }
        
            return isParseSuccessful;
        }

        public bool TryParseCheckSum(StreamReader eAlertCheckSumRecord, out int checkSumValue)
        {
            bool isParseSuccessful = true;
            int recordCount = 0;

            while (eAlertCheckSumRecord.Peek() != -1 && isParseSuccessful)
            {
                isParseSuccessful = int.TryParse(eAlertCheckSumRecord.ReadLine(), out recordCount);
            }

            checkSumValue = recordCount;

            if (!isParseSuccessful) SendErrorLog(ErrorMessageConstants.SendAlertService_InvalidChecksumFileFormat);

            eAlertCheckSumRecord.Close();
            return isParseSuccessful;

        }

        public bool TryParseSmsReportRecords(StreamReader reportRecord, out SmsStatusReport smsStatusReport)
        {
            SmsStatusReport statusReport = new SmsStatusReport();
            bool isParseSuccessful = true;
            string reportLine = null;

            while (reportRecord.Peek() != -1 && isParseSuccessful)
            {
                reportLine = reportRecord.ReadLine();

                if (!String.IsNullOrEmpty(reportLine) && isParseSuccessful)
                {
                    // Current line is Report Date
                    if (reportLine.Contains(ServiceConstants.ReportServiceConstants.ReportDate))
                    {
                        string[] reportInputLine = reportLine.Split(',');
                        isParseSuccessful = reportInputLine.Length == 2; // This is two-parts record, if not, invalid format
                        DateTime parsedReportDate = new DateTime();
                        if(isParseSuccessful) isParseSuccessful = DateTime.TryParse(reportInputLine[1], out parsedReportDate);
                        statusReport.ReportDate = parsedReportDate;

                    }
                    else if (reportLine.Contains(ServiceConstants.ReportServiceConstants.Agency))
                    {
                        string[] reportInputLine = reportLine.Split(',');
                        isParseSuccessful = reportInputLine.Length == 2 ; // This is two-parts record, if not, invalid format
                        statusReport.Agency = reportInputLine[1];
                    }
                    else if (reportLine.Contains(ServiceConstants.ReportServiceConstants.ServiceId))
                    {
                        string[] reportInputLine = reportLine.Split(',');
                        isParseSuccessful = reportInputLine.Length == 2 ; // This is two-parts record, if not, invalid format
                        statusReport.ServiceId = reportInputLine[1];
                    }
                    else if (reportLine.Contains(ServiceConstants.ReportServiceConstants.NoRecord))
                    {
                        // When there is no records inside the Sms report file, to log it into the Db
                        SmsStatus statusRecord = new SmsStatus();
                        statusRecord.TotalSuccessful = 0;
                        statusRecord.TotalPending = 0;
                        statusRecord.TotalFailure = 0;
                        statusRecord.ProcessedFileName = ServiceConstants.ReportServiceConstants.NoRecord;
                        statusRecord.ProcessedDateTime = DateTime.Now;
                        statusReport.SmsStatusRecords.Add(statusRecord);
                    }
                    else
                    {
                        // Could be header, report line or invalid record
                        bool isHeaderRecord = System.Text.RegularExpressions.Regex.IsMatch(reportLine, ServiceConstants.ReportServiceConstants.ReportHeaderExpression);
                        string[] reportInputLine = reportLine.Split(',');
                        if (reportInputLine.Length == 5 && isHeaderRecord)
                        {
                            // This is header record, no need to process
                        }
                        else if (reportInputLine.Length == 5 && !isHeaderRecord)
                        {
                            // This is report line, need to parse
                            SmsStatus statusRecord = new SmsStatus();

                            statusRecord.ProcessedFileName = reportInputLine[0];

                            int totalSuccessful = 0;
                            if (isParseSuccessful) isParseSuccessful = Int32.TryParse(reportInputLine[1], out totalSuccessful);
                            if (isParseSuccessful) statusRecord.TotalSuccessful = totalSuccessful;

                            int totalPending = 0;
                            if (isParseSuccessful) isParseSuccessful = Int32.TryParse(reportInputLine[2], out totalPending);
                            if (isParseSuccessful) statusRecord.TotalPending = totalPending;

                            int totalFailure = 0;
                            if (isParseSuccessful) isParseSuccessful = Int32.TryParse(reportInputLine[3], out totalFailure);
                            if (isParseSuccessful) statusRecord.TotalFailure = totalFailure;

                            DateTime proccessedDate = new DateTime();
                            if (isParseSuccessful) isParseSuccessful = DateTime.TryParseExact(reportInputLine[4], ServiceConstants.DateFormats.YYYYMMDDHMM, new System.Globalization.CultureInfo("en-US"), System.Globalization.DateTimeStyles.None, out proccessedDate);
                            if (isParseSuccessful) statusRecord.ProcessedDateTime = proccessedDate;

                            statusReport.SmsStatusRecords.Add(statusRecord);
                        }
                        else
                        {
                            // Invalid line, this shouldn't be in the file
                            isParseSuccessful = false;
                        }
                    }
                }
            }
            smsStatusReport = statusReport;

            reportRecord.Close();
            return isParseSuccessful;
        }

        private void SendErrorLog(string errorMessage)
        {
            ErrorLogManager errorLogManager = new ErrorLogManager();
            errorLogManager.CreateErrorLogfile(_currentConfiguration,ServiceType.SendAlertService, errorMessage);

        }

        public static IEnumerable<string> SplitCSV(string csvString)
        {
            var sb = new StringBuilder();
            bool quoted = false;

            foreach (char c in csvString)
            {
                if (quoted)
                {
                    if (c == '"')
                        quoted = false;
                    else
                        sb.Append(c);
                }
                else
                {
                    if (c == '"')
                    {
                        quoted = true;
                    }
                    else if (c == ',')
                    {
                        yield return sb.ToString();
                        sb.Length = 0;
                    }
                    else
                    {
                        sb.Append(c);
                    }
                }
            }

            if (quoted)
                throw new ArgumentException("csvString", "Unterminated quotation mark.");

            yield return sb.ToString();
        }

    }
}
